package rainy.file.synchronization.client.file;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import rainy.file.synchronization.config.RainyRunTime;

public class FileTailer extends Thread {

	/**
	 * How frequently to check for file changes; defaults to 5 seconds
	 */
	private long sampleInterval = 1000;

	private String tailFileCharSet = RainyRunTime.getStringPropDefine("rainy.slaves.tail.target.file.encoding",
			"utf-8");

	/**
	 * The log file to tail
	 */
	private File targetFile;

	private String monitoredFolder;

	/**
	 * Defines whether the log file tailer should include the entire contents of
	 * the exising log file or tail from the end of the file when the tailer
	 * starts
	 */
	private boolean startAtBeginning = false;

	/**
	 * Is the tailer currently tailing?
	 */
	private boolean tailing = false;

	/**
	 * Set of listeners
	 */
	private Set<FileTailerListener> listeners = new HashSet<>();
	WatchService watchService;

	/**
	 * Creates a new log file tailer that tails an existing file and checks the
	 * file for updates every 1000ms
	 */
	public FileTailer(File file) {
		this(file, 1000, true);
	}

	/**
	 * Creates a new log file tailer
	 *
	 * @param file
	 *            The file to tail
	 * @param sampleInterval
	 *            How often to check for updates to the log file (default =
	 *            5000ms)
	 * @param startAtBeginning
	 *            Should the tailer simply tail or should it process the entire
	 *            file and continue tailing (true) or simply start tailing from
	 *            the end of the file
	 */
	public FileTailer(File file, long sampleInterval, boolean startAtBeginning) {
		System.out.println("[Tailer monitor] " + file);
		this.targetFile = file;
		this.sampleInterval = sampleInterval;
		Path path = null;
		if (file.isDirectory()) {
			monitoredFolder = targetFile.getAbsolutePath();
		} else {
			monitoredFolder = targetFile.getParent();
		}
		path = Paths.get(monitoredFolder);
		try {
			watchService = path.getFileSystem().newWatchService();
			path.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_MODIFY);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void addLogFileTailerListener(FileTailerListener l) {
		this.listeners.add(l);
	}

	public void removeLogFileTailerListener(FileTailerListener l) {
		this.listeners.remove(l);
	}

	protected void fireNewLogFileLine(String fileName, String line) {
		for (Iterator<FileTailerListener> i = this.listeners.iterator(); i.hasNext();) {
			FileTailerListener l = (FileTailerListener) i.next();
			l.newLogFileLine(fileName, line);
		}
	}

	public void stopTailing() {
		this.tailing = false;
	}

	public void run() {
		long filePointer = 0;

		if (this.startAtBeginning) {
			filePointer = 0;
		} else {
			filePointer = this.targetFile.length();
		}

		try {
			this.tailing = true;
			RandomAccessFile file = new RandomAccessFile(targetFile, "r");
			while (this.tailing) {
				WatchKey key = watchService.take();
				for (WatchEvent<?> event : key.pollEvents()) {
					System.out.println("[Tail monitor ] " + event.context() + "  " + event.kind() + " was triggered");
				}
				long fileLength = this.targetFile.length();
				System.out.println("fileLength : " + fileLength);
				if (fileLength == 0) {
					System.out.println("fileLength : " + fileLength);
					Thread.sleep(sampleInterval);
					System.out.println("Sleep : " + sampleInterval);
					key.reset();
					continue;
				}
				if (fileLength < filePointer && fileLength != 0) {
					file = new RandomAccessFile(targetFile, "r");
					filePointer = 0;
				}
				if (fileLength > filePointer) {
					file.seek(filePointer);
					String line = file.readLine();
					while (line != null) {
						line = new String(line.getBytes("8859_1"), tailFileCharSet);
						this.fireNewLogFileLine(targetFile.getName(), line);
						line = file.readLine();
					}
					filePointer = file.getFilePointer();
				}
				key.reset();
			}
			file.close();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

	}

	public static void main(String[] args) {
		FileTailer tailer = new FileTailer(new File("D:\\logs\\aa.txt"), 1000, true);
		tailer.addLogFileTailerListener(new FileTailerListener());
		tailer.start();
	}
}